package org.fing.edu.uy.esbadp.modules.admMonitoring;

import java.sql.SQLException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.fing.edu.uy.esbadp.constants.EsbAdpPropertyConfig;
import org.fing.edu.uy.esbadp.core.entity.DMAdpMonitorMechanism;
import org.fing.edu.uy.esbadp.core.entity.DMAdpMonitorProperty;
import org.fing.edu.uy.esbadp.core.entity.DMRegisteredService;
import org.fing.edu.uy.esbadp.core.entity.PropertiesMonitoring;
import org.fing.edu.uy.esbadp.core.interfaces.IAdpProperties;
import org.fing.edu.uy.esbadp.core.interfaces.IMechanismMonitor;
import org.fing.edu.uy.esbadp.core.registry.RegistryManager;
import org.fing.edu.uy.esbadp.core.utils.JMXManager;
import org.fing.edu.uy.esbadp.modules.persistance.ServiceManagerFactory;
import org.fing.edu.uy.esbadp.modules.persistance.ServiceManagerInterface;

public class MonitoringAdministrator {
	
	private static java.util.logging.Logger logger = Logger.getLogger(MonitoringAdministrator.class.getSimpleName());
	
	private static MonitoringAdministrator instance = null;
	
	private Map<String,IAdpProperties> propertiesClassInstances;
	
	private MonitoringAdministrator(){
		propertiesClassInstances = new HashMap<String, IAdpProperties>();
	}
	
	public static synchronized MonitoringAdministrator getInstance() {
		if (instance == null) {
			instance = new MonitoringAdministrator();
		}
		return instance;
	}

	public void createPropertiesForServices() throws SQLException {	
		ServiceManagerInterface serviceManager = ServiceManagerFactory.getServiceManager();				
		List<DMRegisteredService> servicios = serviceManager.getAllVirtualServices();	
		RegistryManager registryManager = RegistryManager.getInstance();
		List<DMAdpMonitorMechanism> mechanisms = registryManager.getAdpMonitorMechanism().getMechanisms();
		List<DMAdpMonitorProperty> properties = registryManager.getAdpMonitorProperties().getProperties();
		for(DMRegisteredService service:servicios){			
			HashMap<String,Object> serviceMechanisms = new HashMap<String, Object>();
			//Registered Mechanisms
			for(DMAdpMonitorMechanism mechanism:mechanisms){
				IMechanismMonitor mch = registryManager.getAdpMechanismMonitor(mechanism.getValueName());
				if(mch!=null){
					Object mValue = mch.getMonitorValue(service);
					serviceMechanisms.put(mechanism.getValueName(), mValue);
				}else{
					logger.log(Level.SEVERE, "Exception in MonitoringAdministrator when creating monitor mechanism instance for:" + mechanism.getClassName());	
				}
			}
			//Agrego la propiedad de final al map para que no puedan modificar los valores de los mecanismos
			Collections.unmodifiableMap(serviceMechanisms);
			PropertiesMonitoring propertiesMonitoring = new PropertiesMonitoring();
			//Registered properties.
			for(DMAdpMonitorProperty property:properties){	
				IAdpProperties prop;
				if(this.containsPropertyClass(property.getPropertyName())){
					prop = this.getPropertyClass(property.getPropertyName());					
				}else{
					prop = registryManager.getAdpMonitorProperty(property.getPropertyName());
					this.putPropertyClass(property.getPropertyName(), prop);
				}
				if(prop!=null){
					Object pValue = prop.getPropertyValue(serviceMechanisms, service);
					if (pValue != null){
						propertiesMonitoring.addPropertyMonitoring(property.getPropertyName(), pValue);
					}
				}else{
					logger.log(Level.SEVERE, "Exception in MonitoringAdministrator when creating properties instance for:" + property.getClassName());
				}
			}
			
			notifyPropertiesMotorMonitor(service.getServiceID(), propertiesMonitoring);
		}
	}

	/*** Auxiliary functions ***/
	
	private IAdpProperties getPropertyClass(String propertyName){
		return this.propertiesClassInstances.get(propertyName);
	}
	
	private boolean containsPropertyClass(String propertyName){
		return this.propertiesClassInstances.containsKey(propertyName);
	}
	
	private IAdpProperties putPropertyClass(String propertyName , IAdpProperties propertyClass){
		return this.propertiesClassInstances.put(propertyName , propertyClass);
	}
	
	private void notifyPropertiesMotorMonitor(long serviceID, PropertiesMonitoring propertiesMonitoring) {
		try{
			String jmxServiceURL = RegistryManager.getInstance().getAdpConfigProperty(EsbAdpPropertyConfig.motorMonitorURL, "");
			String mBeanName = RegistryManager.getInstance().getAdpConfigProperty(EsbAdpPropertyConfig.motorMonitorObjectName, "");
			String operName = "notifyProperties";
			Object[] arguments = {serviceID, propertiesMonitoring};
			String[] signature = {Long.class.getName(), PropertiesMonitoring.class.getName()};
			JMXManager.callOperation(jmxServiceURL, mBeanName, operName, arguments, signature);
		}catch (Exception ex) {
			logger.log(Level.SEVERE, "Error when send properties to MotorMonitor. Service Id: " + serviceID, ex);
		}
	}
	
}
